/*
 * MPEGSideInfo.cpp
 *        -- implementation of side information format for MPEG frame
 *
 * Author: Qiu Shuang
 * Date: Mar 2nd, 2011
 */
#include <string.h>
#include <assert.h>
#include "MPEGSideInfo.h"
#include "MPEGFrameHeader.h"

/* constructor */
MPEGSideInfo::MPEGSideInfo(MPEGFrameHeader *header, FILE *mpeg):_mpeg(mpeg),
    _header(header), _bits_read(0){
    /* populate buffer */
    fread(_buffer, sizeof(unsigned char), SIDEINFO_SIZE, _mpeg);

    /* fill high dimensional and conditional populated data structure with 0 */
    memset((void*)_table_select, 0, sizeof(unsigned char) * GRANULE * CHANNEL * PURELONGWIN_COUNTS);
    memset((void*)_shortwin_gain, 0, sizeof(unsigned char) * GRANULE * CHANNEL * SUBBLOCK_COUNTS);
    memset((void*)_mixed_window, 0, sizeof(bool) * GRANULE * CHANNEL);

    decode(NULL);

    /* side information containing 32 bytes */
    assert(_bits_read == 32 * BITS_PER_BYTE);
}

/* destructor, no special operation required */
MPEGSideInfo::~MPEGSideInfo(){
}

/* initialize static constant variable */
unsigned char MPEGSideInfo::_buffer[SIDEINFO_SIZE];

const unsigned int MPEGSideInfo::PREEMPHASIS_TABLE[CRITICAL_BANDS] =
        {0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,2,2,3,3,3,2,0};

/* decode sideinfo in each frame
 *  structure of sideinfo
 *      main data begin : 9 -- offset in byte specifying where the main
 *                             data for the frame(logical frame) begins.
 *  private bits : 3 -- only MPEG1-layerIII encoding is handled, which use
 *                      3 bits for padding
 *  scalefactor selection information : 1 * GRANULE * CHANNEL --
 *              specifies for the scalefactor classes of each channel if the
 *              scalefactors for the first granule are reused for scalefactors
 *              and Huffman-encoded samples.
 *  part2 3 length : 12 * GRANULE * CHANNEL -- specifies the number of bits
 *                                             used for scalefactors and Huffman
 *                                             encoded samples.
 *  big values : 9 * GRANULE * CHANNEL -- specifies the number of samples pairs
 *                                        in the big value region.
 *                                        quadruples in the count1-region must
 *                                        deduced from part2_3_length and big_values
 *                                        during decoding.
 *  global gain : 8 * GRANULE * CHANNEL -- quantizer step size.
 *  scalefac compress : 4 * GRANULE * CHANNEL -- specifies number of bits used for
 *                                        scalefactor.
 *  window_switching : 1 * GRANULE * CHANNEL -- mixed window?
 *  region0_count : 4 * GRANULE * CHANNEL -- samples number in region0
 *  region1_count : 3 * GRANULE * CHANNEL -- samples number in region1
 *  window type : 2 * GRANULE * CHANNEL
 *  mixed window : 1 * GRANULE * GHANNEL
 *  shortwin gain : 3 * GRANULE * CHANNEL
 *  and some utility bits
 **/
int MPEGSideInfo::decode(const unsigned char *buf){
    if(_mpeg == NULL){
        printf("source file unavailable.\n");
    }else{
    /* verfication on MPEG channel number and version number performed on upper level
     * only MPEG1-layerIII and stereo channel encoding supported */

        /* start of main data */
        _main_data_begin = (unsigned short)readbits(9);
        _padding_bit = (unsigned char)readbits(3);         /* only stereo MPEG1-layerIII is acceptable */

        int granule, channel, scalefactor_class;
        for(channel = 0; channel < CHANNEL; channel++){
            for(scalefactor_class = 0; scalefactor_class < SCALEFACTOR_CLASS; scalefactor_class++){
                /* scalefactor selection information */
                _scalefactor_selection[channel][scalefactor_class] = (bool)readbits(1);
            }
        }

        for(granule = 0; granule < GRANULE; granule++){
            for(channel = 0; channel < CHANNEL; channel++){
                /* number of main_data bits including number of bits for scalefactors and huffman code */
                _part2_3_length[granule][channel] = (unsigned short)readbits(12);
                /* number of value pairs at low frequencies */
                _big_values[granule][channel] = (unsigned short)readbits(9);
                /* quantizer step size information */
                _global_gain[granule][channel] = (unsigned char)readbits(8);
                /* number of bits used for scalefactor */
                _scalefac_compress[granule][channel] = (unsigned char)readbits(4);
                /* window switching flat */
                _window_switching[granule][channel] = (bool)readbits(1);

                if(_window_switching[granule][channel]){    /* not pure long window */
                    _window_type[granule][channel] = (unsigned char)readbits(2);
                    _mixed_window[granule][channel] = (bool)readbits(1);

                    /* which huffman table to use in area of big_values */
                    for(scalefactor_class = 0; scalefactor_class < MIXEDLONGWIN_COUNTS;
                        scalefactor_class++){
                            _table_select[granule][channel][scalefactor_class]
                                = (unsigned char)readbits(5);
                    }

                    /* gain offset from global gain with short window */
                    for(scalefactor_class = 0; scalefactor_class < 3;
                        scalefactor_class++){
                            _shortwin_gain[granule][channel][scalefactor_class]
                                = (unsigned char)readbits(3);
                    }

                    /* set region_count parameters, which are implicit in this case */
                    if(_window_type[granule][channel] == 0){
                        printf("Bad side info: window type 0 in split window\n");
                    }else if(_window_type[granule][channel] == 2
                        && _mixed_window[granule][channel]){
                            _region0_samples[granule][channel] = (unsigned char)8; /* MI 9; */
                    }else{
                        _region0_samples[granule][channel] = (unsigned char)7;     /* MI 8; */
                    }

                    _region1_samples[granule][channel] = (unsigned char)(20 - _region0_samples[granule][channel]);
                }else{      /* pure long window used */
                    _window_type[granule][channel] = 0;             /* indicating pure long window used */
                    _mixed_window[granule][channel] = 0;

                    /* which huffman table to use in area of big_values */
                    for(scalefactor_class = 0; scalefactor_class < PURELONGWIN_COUNTS;
                        scalefactor_class++){
                            _table_select[granule][channel][scalefactor_class]
                                = (unsigned char)readbits(5);
                    }

                    /* spectrum partitioning */
                    _region0_samples[granule][channel] = (unsigned char)readbits(4);
                    _region1_samples[granule][channel] = (unsigned char)readbits(3);
                }

                /* additional high frequency amplification */
                _preflag[granule][channel] = !!readbits(1);
                _scalefac_scale[granule][channel] = !!readbits(1);
                _count1_table_select[granule][channel] = !!readbits(1);
            }
        }
    }
    /* MPEG2 file is not taken into consideration */

    return true;
}

/* get decoded side information for MPEG1-layer3 */
const unsigned char *MPEGSideInfo::decodedContent() const{
    return NULL;
}

unsigned int MPEGSideInfo::readbits(int num){
    int byte_read = _bits_read / BITS_PER_BYTE;
    int bit_left = 0;

    unsigned int ret_value = 0;
    while(num > 0){
        byte_read = _bits_read / BITS_PER_BYTE;     /* update read bytes */
        bit_left = BITS_PER_BYTE - _bits_read % BITS_PER_BYTE;
        int extract = MIN(bit_left, MIN(num, BITS_PER_BYTE));
        unsigned char tmp = 0;              /* one byte read a time at most */
        unsigned char mask = 0xff;

        mask >>= _bits_read % BITS_PER_BYTE;
        tmp = _buffer[byte_read] & mask;
        tmp >>= bit_left - extract;

        // append to return value
        ret_value <<= extract;
        ret_value |= tmp;

        num = num - extract;
        _bits_read = _bits_read + extract;
    }

    return ret_value;
}

unsigned int MPEGSideInfo::region0Samples(unsigned int granule, unsigned int channel){
    if(bigValue(granule, channel) > 0){
        if (windowSwitching(granule, channel) &&
            (windowType(granule, channel) == 2)){           /* pure short windows */
                return 36;
        }else{
            return MIN(_header->longWinScalefactorBandBound(region0Count(granule, channel) + 1),
                bigValue(granule, channel) * 2);
        }
    }else{
        return 0;
    }
}

unsigned int MPEGSideInfo::region1Samples(unsigned int granule, unsigned int channel){
    if (bigValue(granule, channel) > 0){
        /* pure short window */
        if (windowSwitching(granule, channel) && ((windowType(granule, channel) == 2))){
            return (2*bigValue(granule, channel) - 36);
        }else{
            return (MIN(_header->longWinScalefactorBandBound(region0Count(granule, channel) +
                region1Count(granule, channel) + 2), bigValue(granule, channel) * 2) -
                region0Samples(granule, channel));
        }
    } else {
        return 0;
    }
}


#ifdef DEBUG
void printInfo(int granule, int channel, unsigned int data, const char *field){
    printf("%s of channel[%d][%d]: %d\n", field, granule, channel, data);
}

//void MPEGSideInfo::printSideInfo(){
//    printf("MPEG layerIII sideinfo.\n");
//    printf("main data begins at %d\n", _main_data_begin);
//
//    int granule, channel;
//    for(granule = 0; granule < GRANULE; granule++){
//        for(channel = 0; channel < CHANNEL; channel++){
//            printInfo(granule, channel, _scalefactor_selection[granule][channel], "scale_factor_selection");
//            printInfo(granule, channel, _part2_3_length[granule][channel], "part2_3_field");
//            printInfo(granule, channel, _big_values[granule][channel], "big_value");
//            printInfo(granule, channel, _global_gain[granule][channel], "global_gain");
//            printInfo(granule, channel, _scalefac_compress[granule][channel], "scalefac_compress");
//            printInfo(granule, channel, _window_switching[granule][channel], "window_switching");
//            int i;
//            for(i = 0; i < 3; i++)
//                printInfo(granule, channel, _table_select[granule][channel][i], "table_selected");
//            printInfo(granule, channel, _region0_samples[granule][channel], "region0_count");
//            printInfo(granule, channel, _region1_samples[granule][channel], "region1_count");
//            printInfo(granule, channel, _window_type[granule][channel], "window_type");
//            printInfo(granule, channel, _mixed_window[granule][channel], "mixed_window");
//            for(i = 0; i < 3; i++)
//                printInfo(granule, channel, _shortwin_gain[granule][channel][i], "shortwin_gain");
//
//            printInfo(granule, channel, _preflag[granule][channel], "preflag");
//            printInfo(granule, channel, _scalefac_scale[granule][channel], "scalefac_scale");
//            printInfo(granule, channel, _count1_table_select[granule][channel], "count1_table_select");
//        }
//    }
//}

#include <fstream>
using namespace std;

 void 
MPEGSideInfo::printSideInfo(ofstream &output)
{
  output << "\tLayer 3 Side information:" << endl;
  output << "\tMain_data_begin=" << _main_data_begin << endl;
  output << "\tPrivate_bits="    << (int)_padding_bit    << endl;
  const int s_ch = CHANNEL;
  int ch, area;
  unsigned int scfc;
  int gr, sfb;
  for (ch=0; ch<s_ch; ch++) {
    for (scfc=0; scfc<4; scfc++) {
      output << "\tscfsi[" << ch << "][" << scfc << "]="
	   << (int)scalefactorSelection(ch,scfc) << endl;
    }
    for (unsigned gr=0; gr<GRANULE; gr++) {
      output << "\tpart2_3_length[" << ch << "][" << gr << "]="
	   << (int)part2_3_length(gr,ch) << endl;
      output << "\tbig_values[" << ch << "][" << gr << "]="
	   << (int)bigValue(gr,ch) << endl;
      output << "\tglobal_gain[" << ch << "][" << gr << "]="
	   << (int)globalGain(gr,ch) << endl;
      output << "\tscalefac_compress[" << ch << "][" << gr << "]="
	   << (int)scalefactorCompress(gr,ch) << endl;
      output << "\twindow_switching[" << ch << "][" << gr << "]="
	   << (int)windowSwitching(gr,ch) << endl;
      output << "\tblocktype[" << ch << "][" << gr << "]="
	   << (int)windowType(gr,ch) << endl;
      output << "\tmixedblock[" << ch << "][" << gr << "]="
	   << (int)mixedWindow(gr,ch) << endl;
      for (area=0; area<3; area++) {
	output << "\ttable_select[" << ch << "][" << gr << "][" 
	     << area << "]=" << (int)tableSelect(gr,ch,area) << endl;
	output << "\tsubblock_gain[" << ch << "][" << gr << "][" 
	     << area << "]=" << (int)shortwinGain(gr,ch,area) << endl;
      }
      output << "\tregion0_count[" << ch << "][" << gr << "]="
	   << (int)region0Count(gr,ch) << endl;
      output << "\tregion1_count[" << ch << "][" << gr << "]="
	   << (int)region1Count(gr,ch) << endl;
      output << "\tpreflag[" << ch << "][" << gr << "]="
	   << (int)preflag(gr,ch) << endl;
      output << "\tscalefac_scale[" << ch << "][" << gr << "]="
	   << (int)scalefacScale(gr,ch) << endl;
      output << "\tcount1table_select[" << ch << "][" << gr << "]="
	   << (int)count1TableSelect(gr,ch) << endl;
      output << "\tregion0_samps[" << ch << "][" << gr << "]="
	   << region0Samples(gr,ch) << endl;
      output << "\tregion1_samps[" << ch << "][" << gr << "]="
	   << region1Samples(gr,ch) << endl;
//      output << "\tregion2_samps[" << ch << "][" << gr << "]="
//	   << region2Samples(gr,ch) << endl;
//      output << "\tcount1_values[" << ch << "][" << gr << "]="
//	   << count1_values(ch,gr) << endl;
 //     output << "\tslen1[" << ch << "][" << gr << "]="
	//   << slen1(ch,gr) << endl;
 //     output << "\tslen2[" << ch << "][" << gr << "]="
	//   << slen2(ch,gr) << endl;
 //     output << "\tscalefacs[" << ch << "][" << gr << "]=";
 //     if (window_switching(ch,gr) && (blocktype(ch,gr)==2)) {
	//if (mixedblock(ch,gr)) { // mixed
	//  for (sfb=0; sfb<8; sfb++) { // long
	//    output << " " << scalefac[ch][gr].l[sfb];
	//  }
	//  for (sfb=3; sfb<13; sfb++) { // short
	//    output << " " << scalefac[gr][ch].s[0][sfb];
	//    output << " " << scalefac[gr][ch].s[1][sfb];
	//    output << " " << scalefac[gr][ch].s[2][sfb];
	//  }
	//} else {
	//  for (sfb=0; sfb<13; sfb++) { // short only
	//    output << " " << scalefac[gr][ch].s[0][sfb];
	//    output << " " << scalefac[gr][ch].s[1][sfb];
	//    output << " " << scalefac[gr][ch].s[2][sfb];
	//  }
	//}
 //     } else {
	//for (sfb=0; sfb<21; sfb++) { // long
	//  output <<  " " << scalefac[gr][ch].l[sfb];
	//}
 //     }
 //     output << endl;
    }
  }
  return;
}
#endif